<!DOCTYPE html>
<html class="writer-html5" lang="zh" >
<head>
  <meta charset="utf-8" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />

  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Ch1-1 创建GLFW窗口 &mdash; EasyVulkan</title>
      <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
      <link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
      <link rel="stylesheet" href="_static/theme.css" type="text/css" />
  <!--[if lt IE 9]>
    <script src="_static/js/html5shiv.min.js"></script>
  <![endif]-->
  
        <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
        <script src="_static/jquery.js"></script>
        <script src="_static/underscore.js"></script>
        <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
        <script src="_static/doctools.js"></script>
    <script src="_static/js/theme.js"></script>
    <link rel="index" title="索引" href="genindex.html" />
    <link rel="search" title="搜索" href="search.html" />
    <link rel="next" title="Ch1-2 初始化流程" href="Ch1-2%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%B5%81%E7%A8%8B.html" />
    <link rel="prev" title="Ch1-0 准备工作" href="Ch1-0%20%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C.html" /> 
</head>

<body class="wy-body-for-nav"> 
  <div class="wy-grid-for-nav">
    <nav data-toggle="wy-nav-shift" class="wy-nav-side">
      <div class="wy-side-scroll">
        <div class="wy-side-nav-search" >
            <a href="index.html" class="icon icon-home"> EasyVulkan
            <img src="_static/logo1.png" class="logo" alt="Logo"/>
          </a>
<div role="search">
  <form id="rtd-search-form" class="wy-form" action="search.html" method="get">
    <input type="text" name="q" placeholder="在文档中搜索" />
    <input type="hidden" name="check_keywords" value="yes" />
    <input type="hidden" name="area" value="default" />
  </form>
</div>
        </div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
              <p class="caption" role="heading"><span class="caption-text">第一章 初始化</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="Ch1-0%20%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C.html">Ch1-0 准备工作</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Ch1-1 创建GLFW窗口</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#id1">包含并链接必要的文件</a></li>
<li class="toctree-l2"><a class="reference internal" href="#id2">创建窗口</a></li>
<li class="toctree-l2"><a class="reference internal" href="#id3">在窗口标题上显示帧率</a></li>
<li class="toctree-l2"><a class="reference internal" href="#id4">试着运行一下吧！</a></li>
<li class="toctree-l2"><a class="reference internal" href="#id5">在全屏和窗口模式间切换</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-2%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%B5%81%E7%A8%8B.html">Ch1-2 初始化流程</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-3%20%E5%88%9B%E5%BB%BAVK%E5%AE%9E%E4%BE%8B%E4%B8%8E%E9%80%BB%E8%BE%91%E8%AE%BE%E5%A4%87.html">Ch1-3 创建VK实例与逻辑设备</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-4%20%E5%88%9B%E5%BB%BA%E4%BA%A4%E6%8D%A2%E9%93%BE.html">Ch1-4 创建交换链</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第二章 绘制一个三角形</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch2-0%20%E4%BB%A3%E7%A0%81%E6%95%B4%E7%90%86%E5%8F%8A%E4%B8%80%E4%BA%9B%E8%BE%85%E5%8A%A9%E7%B1%BB.html">Ch2-0 代码整理及一些辅助类</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch2-1%20Rendering%20Loop.html">Ch2-1 Rendering Loop</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch2-2%20%E5%88%9B%E5%BB%BA%E6%B8%B2%E6%9F%93%E9%80%9A%E9%81%93%E5%92%8C%E5%B8%A7%E7%BC%93%E5%86%B2.html">Ch2-2 创建渲染通道和帧缓冲</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch2-3%20%E5%88%9B%E5%BB%BA%E7%AE%A1%E7%BA%BF%E5%B9%B6%E7%BB%98%E5%88%B6%E4%B8%89%E8%A7%92%E5%BD%A2.html">Ch2-3 创建管线并绘制三角形</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第三章 纵观Vulkan</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch3-1%20%E5%90%8C%E6%AD%A5%E5%8E%9F%E8%AF%AD.html">Ch3-1 同步原语</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-2%20%E5%9B%BE%E5%83%8F%E4%B8%8E%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch3-2 图像与缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-3%20%E7%AE%A1%E7%BA%BF%E5%B8%83%E5%B1%80%E5%92%8C%E7%AE%A1%E7%BA%BF.html">Ch3-3 管线布局和管线</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-4%20%E6%B8%B2%E6%9F%93%E9%80%9A%E9%81%93%E5%92%8C%E5%B8%A7%E7%BC%93%E5%86%B2.html">Ch3-4 渲染通道和帧缓冲</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-5%20%E5%91%BD%E4%BB%A4%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch3-5 命令缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-6%20%E6%8F%8F%E8%BF%B0%E7%AC%A6.html">Ch3-6 描述符</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-7%20%E9%87%87%E6%A0%B7%E5%99%A8.html">Ch3-7 采样器</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-8%20%E6%9F%A5%E8%AF%A2.html">Ch3-8 查询</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第四章 着色器</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch4-1%20%E7%9D%80%E8%89%B2%E5%99%A8%E6%A8%A1%E7%BB%84.html">Ch4-1 着色器模组</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch4-2%20%E9%A1%B6%E7%82%B9%E7%9D%80%E8%89%B2%E5%99%A8.html">Ch4-2 顶点着色器</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch4-3%20%E7%89%87%E6%AE%B5%E7%9D%80%E8%89%B2%E5%99%A8.html">Ch4-3 片段着色器</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch4-4%20%E5%87%A0%E4%BD%95%E7%9D%80%E8%89%B2%E5%99%A8.html">Ch4-4 几何着色器</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第五章 封装常用对象</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch5-0%20VKBase%2B.h.html">Ch5-0 VKBase+.h</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch5-1%20%E5%90%84%E7%A7%8D%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch5-1 各种缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch5-2%202D%E8%B4%B4%E5%9B%BE%E5%8F%8A%E7%94%9F%E6%88%90Mipmap.html">Ch5-2 2D贴图及生成Mipmap</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch5-3%202D%E8%B4%B4%E5%9B%BE%E6%95%B0%E7%BB%84.html">Ch5-3 2D贴图数组</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第六章 进阶Vulkan</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch6-0%20%E4%BD%BF%E7%94%A8%E6%96%B0%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7.html">Ch6-0 使用新版本特性</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch6-1%20%E6%97%A0%E5%9B%BE%E5%83%8F%E5%B8%A7%E7%BC%93%E5%86%B2.html">Ch6-1 无图像帧缓冲</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch6-2%20%E5%8A%A8%E6%80%81%E6%B8%B2%E6%9F%93.html">Ch6-2 动态渲染</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第七章 基础示例</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch7-1%20%E5%88%9D%E8%AF%86%E9%A1%B6%E7%82%B9%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch7-1 初识顶点缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-2%20%E5%88%9D%E8%AF%86%E7%B4%A2%E5%BC%95%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch7-2 初识索引缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-3%20%E5%88%9D%E8%AF%86%E5%AE%9E%E4%BE%8B%E5%8C%96%E7%BB%98%E5%88%B6.html">Ch7-3 初识实例化绘制</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-4%20%E5%88%9D%E8%AF%86Push%20Constant.html">Ch7-4 初识Push Constant</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-5%20%E5%88%9D%E8%AF%86Uniform%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch7-5 初识Uniform缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-6%20%E6%8B%B7%E8%B4%9D%E5%9B%BE%E5%83%8F%E5%88%B0%E5%B1%8F%E5%B9%95.html">Ch7-6 拷贝图像到屏幕</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-7%20%E4%BD%BF%E7%94%A8%E8%B4%B4%E5%9B%BE.html">Ch7-7 使用贴图</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第八章 简单示例</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch8-1%20%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93.html">Ch8-1 离屏渲染</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch8-2%20%E6%B7%B1%E5%BA%A6%E6%B5%8B%E8%AF%95%E5%92%8C%E6%B7%B1%E5%BA%A6%E5%8F%AF%E8%A7%86%E5%8C%96.html">Ch8-2 深度测试和深度可视化</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch8-3%20%E5%BB%B6%E8%BF%9F%E6%B8%B2%E6%9F%93.html">Ch8-3 延迟渲染</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch8-4%20%E9%A2%84%E4%B9%98Alpha.html">Ch8-4 预乘Alpha</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch8-5%20sRGB%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4%E4%B8%8E%E5%BC%80%E5%90%AFHDR.html">Ch8-5 sRGB色彩空间与开启HDR</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">附录</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ap1-1%20%E8%BF%90%E8%A1%8C%E6%9C%9F%E7%BC%96%E8%AF%91GLSL.html">Ap1-1 运行期编译GLSL</a></li>
</ul>

        </div>
      </div>
    </nav>

    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
          <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
          <a href="index.html">EasyVulkan</a>
      </nav>

      <div class="wy-nav-content">
        <div class="rst-content">
          <div role="navigation" aria-label="Page navigation">
  <ul class="wy-breadcrumbs">
      <li><a href="index.html" class="icon icon-home"></a> &raquo;</li>
      <li>Ch1-1 创建GLFW窗口</li>
      <li class="wy-breadcrumbs-aside">
      </li>
  </ul>
  <hr/>
</div>
          <div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
           <div itemprop="articleBody">
             
  <section id="ch1-1-glfw">
<h1>Ch1-1 创建GLFW窗口<a class="headerlink" href="#ch1-1-glfw" title="Permalink to this heading"></a></h1>
<p>
    本章的示例代码参见：<a href="https://github.com/EasyVulkan/EasyVulkan.github.io/blob/main/solution/EasyVulkan_Ch1">EasyVulkan_Ch1</a>
</p>
<p>
    关于本节，如果你倾向于直接使用WindowsAPI来创建窗口，你也可以参考示例代码中的<a href="https://github.com/EasyVulkan/EasyVulkan.github.io/blob/main/solution/EasyVulkan_Ch8/WinGeneral.hpp">WinGeneral.hpp</a>。
</p><section id="id1">
<h2>包含并链接必要的文件<a class="headerlink" href="#id1" title="Permalink to this heading"></a></h2>
<p>
    在一切开始之前，来创建一个用于包含各种库的头文件，名称随你喜欢，我就决定叫它<span class="path">EasyVKStart.h</span>。
    <br>
    然后加入以下代码：
</p>
<pre class="code">
<span class="pragma">#pragma once</span>
<span class="cmt">//可能会用上的C++标准库</span>
<span class="pragma">#include</span> <span class="str">&lt;iostream&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;fstream&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;sstream&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;vector&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;stack&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;map&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;unordered_map&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;span&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;memory&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;functional&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;concepts&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;format&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;chrono&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;numeric&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;numbers&gt;</span>

<span class="cmt">//GLM</span>
<span class="pragma">#define</span> <span class="mcr">GLM_FORCE_DEPTH_ZERO_TO_ONE</span>
<span class="cmt">//如果你惯用左手坐标系，在此定义GLM_FORCE_LEFT_HANDED</span>
<span class="pragma">#include</span> <span class="str">&lt;glm.hpp&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;gtc/matrix_transform.hpp&gt;</span>

<span class="cmt">//stb_image.h</span>
<span class="pragma">#include</span> <span class="str">&lt;stb_image.h&gt;</span>

<span class="cmt">//Vulkan</span>
<span class="pragma">#ifdef</span> <span class="mcr">_WIN32</span>                        <span class="cmt">//考虑平台是Windows的情况（请自行解决其他平台上的差异）</span>
<span class="pragma">#define</span> <span class="mcr">VK_USE_PLATFORM_WIN32_KHR</span>    <span class="cmt">//在包含vulkan.h前定义该宏，会一并包含vulkan_win32.h和windows.h</span>
<span class="pragma">#define</span> <span class="mcr">NOMINMAX</span>                     <span class="cmt">//定义该宏可避免windows.h中的min和max两个宏与标准库中的函数名冲突</span>
<span class="pragma">#pragma comment</span>(<span class="pragma">lib</span>, <span class="str">&quot;vulkan-1.lib&quot;</span>) <span class="cmt">//链接编译所需的静态存根库</span>
<span class="pragma">#endif</span>
<span class="pragma">#include</span> <span class="str">&lt;vulkan/vulkan.h&gt;</span>
</pre>
<ul>
    <li>
        <p>
            GLM本为是OpenGL设计的，在OpenGL中，NDC（标准化设备坐标系）的深度范围为[-1, 1]，而Vulkan中这个范围为[0, 1]，因此我们必须用宏<span class="mcr">GLM_FORCE_DEPTH_ZERO_TO_ONE</span>来指定深度范围，这样才能获得正确的投影矩阵。
        </p>
    </li>
</ul>
<p>
    由于你以后可能会想要将你的程序移植到其他平台，出于通用性的考虑，GLFW相关的内容会被单独拿出来。
</p>
<p>
    接着创建<span class="path">VKBase.h</span>，该文件会用来写Vulkan相关的基本内容。
    <br>
    在<span class="path">VKBase.h</span>中加入以下代码：
</p>
<pre class="code">
<span class="pragma">#pragma once</span>
<span class="pragma">#include</span> <span class="str">&quot;EasyVKStart.h&quot;</span>
<span class="cmt">//定义vulkan命名空间，之后会把Vulkan中一些基本对象的封装写在其中</span>
<span class="kw">namespace</span> vulkan {

}
</pre>
<p>
    最后创建一个<span class="path">GlfwGeneral.hpp</span>，顾名思义，这个hpp用来写一些GLFW的通用内容及相关的全局变量。
    <br>
    在<span class="path">GlfwGeneral.hpp</span>中加入以下代码：
</p>
<pre class="code">
<span class="pragma">#include</span> <span class="str">&quot;VKBase.h&quot;</span>
<span class="pragma">#define</span> <span class="mcr">GLFW_INCLUDE_VULKAN</span>
<span class="pragma">#include</span> <span class="str">&lt;GLFW/glfw3.h&gt;</span>
<span class="pragma">#pragma comment</span>(<span class="pragma">lib</span>, <span class="str">&quot;glfw3.lib&quot;</span>) <span class="cmt">//链接编译所需的静态库</span>
</pre>
<ul>
    <li>
        <p>
            要将GLFW用于Vulkan，必须在包含GLFW之前，定义<span class="mcr">GLFW_INCLUDE_VULKAN</span>宏。
        </p>
    </li>
    <li>
        <p>
            如果你在编译时使用静态链接，则这里的<span class="path">glfw3.lib</span>改为<span class="path">glfw3_mt.lib</span>。
        </p>
    </li>
</ul>
<p>
    想必以上代码无需多做解释，全部抄完后，如果VS提示你有任何文件打不开，检查一下你的附加包含目录和附加库目录。
</p></section>
<section id="id2">
<h2>创建窗口<a class="headerlink" href="#id2" title="Permalink to this heading"></a></h2>
<p>
    在<span class="path">GlfwGeneral.hpp</span>添加以下全局变量：
</p>
<pre class="code">
<span class="cmt">//窗口的指针，全局变量自动初始化为NULL</span>
<span class="type">GLFWwindow</span>* pWindow;
<span class="cmt">//显示器信息的指针</span>
<span class="type">GLFWmonitor</span>* pMonitor;
<span class="cmt">//窗口标题</span>
<span class="kw">const char</span>* windowTitle = <span class="str">&quot;EasyVK&quot;</span>;
</pre>
<p>接着来写两个函数</p>
<pre class="code">
<span class="kw">bool</span> <span class="fn">InitializeWindow</span>(<span class="type">VkExtent2D</span> <span class="par">size</span>, <span class="kw">bool</span> <span class="par">fullScreen</span> = <span class="kw">false</span>, <span class="kw">bool</span> <span class="par">isResizable</span> = <span class="kw">true</span>, <span class="kw">bool</span> <span class="par">limitFrameRate</span> = <span class="kw">true</span>) {
<span class="cmt">/*待后续填充*/</span>
}
<span class="kw">void</span> <span class="fn">TerminateWindow</span>() {
<span class="cmt">/*待后续填充*/</span>
}
</pre>
<p>
    <span class="fn">InitializeWindow</span>(...)用于初始化窗口，我打算让它在初始化成功时返回<span class="kw">true</span>，否则返回<span class="kw">false</span>。
</p>
<table class="docutils align-default">
    <colgroup>
        <col style="width: 30%">
        <col style="width: 70%">
    </colgroup>
    <thead>
        <tr class="row-odd">
            <th class="head" colspan="2"><p>参数说明</p></th>
        </tr>
    </thead>
    <tbody>
        <tr class="row-even">
            <td><p>size</p></td>
            <td><p>窗口大小</p></td>
        </tr>
        <tr class="row-odd">
            <td><p>fullScreen</p></td>
            <td><p>指定是否以全屏初始化窗口</p></td>
        </tr>
        <tr class="row-even">
            <td><p>isResizable</p></td>
            <td><p>指定窗口是否可拉伸，游戏窗口通常是不可任意拉伸的</p></td>
        </tr>
        <tr class="row-odd">
            <td><p>limitFrameRate</p></td>
            <td><p>指定是否将帧数限制到不超过屏幕刷新率，在本节先不实现这个参数的作用</p></td>
        </tr>
    </tbody>
</table>
<p>
    <span class="fn">TerminateWindow</span>(...)用于终止窗口时，清理GLFW。
</p>
<p>
    接着来填充<span class="fn">InitializeWindow</span>(...)。
    <br>
    首先用<a href="https://www.glfw.org/docs/latest/group__init.html#ga317aac130a235ab08c6db0834907d85e">glfwInit</a>(...)初始化GLFW，该函数在执行成功时返回<span class="kw">true</span>。
</p>
<pre class="code">
<span class="kw">if</span> (!<span class="fn">glfwInit</span>()) {
    std::cout &lt;&lt; std::<span class="fn">format</span>(<span class="str">&quot;[ InitializeWindow ] ERROR\nFailed to initialize GLFW!\n&quot;</span>);
    <span class="kw">return false</span>;
}
</pre>
<ul>
    <li>
        <p>
            这里用<code>std::cout &lt;&lt; std::<span class="fn">format</span>(<span class="str">&quot;...&quot;</span>);</code>输出字符串字面量是为了同带更多参数的格式化输出<code>std::cout &lt;&lt; std::<span class="fn">format</span>(<span class="str">&quot;...&quot;</span>, ...);</code>统一，而这是为了方便日后把<code>std::cout &lt;&lt; std::<span class="fn">format</span>(...);</code>一次性替换成<code><span class="fn">某个格式化输出函数</span>(...);</code>，比如会在C++23上线的<a href="https://en.cppreference.com/w/cpp/io/print">print</a>(...)。
        </p>
    </li>
</ul>
<p>
    然后，在创建窗口前，必须调用<a href="https://www.glfw.org/docs/latest/group__init.html#ga110fd1d3f0412822b4f1908c026f724a">glfwWindowHint</a>(<span class="mcr">GLFW_CLIENT_API</span>, <span class="mcr">GLFW_NO_API</span>)。GLFW同GLM一样最初是为OpenGL设计的，<span class="mcr">GLFW_CLIENT_API</span>的默认设置是<span class="mcr">GLFW_OPENGL_API</span>，这种情况下，GLFW会在创建窗口时创建OpenGL的上下文，这对于Vulkan而言是多余的，所以向GLFW说明不需要OpenGL的API。
</p>
<pre class="code">
<span class="fn">glfwWindowHint</span>(<span class="mcr">GLFW_CLIENT_API</span>, <span class="mcr">GLFW_NO_API</span>);
</pre>
<p>
    接着用<a href="https://www.glfw.org/docs/latest/group__init.html#ga110fd1d3f0412822b4f1908c026f724a">glfwWindowHint</a>(<span class="mcr">GLFW_RESIZABLE</span>, <span class="par">isResizable</span>)指定窗口可否拉伸：
</p>
<pre class="code">
<span class="fn">glfwWindowHint</span>(<span class="mcr">GLFW_RESIZABLE</span>, <span class="par">isResizable</span>);
</pre>
<p>
    然后便能用<a href="https://www.glfw.org/docs/latest/group__window.html#ga3555a418df92ad53f917597fe2f64aeb">glfwCreateWindow</a>(...)创建窗口了，该函数在执行成功时返回一非空指针。
</p>
<pre class="code">
pWindow = <span class="fn">glfwCreateWindow</span>(<span class="par">size</span>.width, <span class="par">size</span>.height, windowTitle, <span class="kw">nullptr</span>, <span class="kw">nullptr</span>);
</pre>
<p>
    想必<a href="https://www.glfw.org/docs/latest/group__window.html#ga3555a418df92ad53f917597fe2f64aeb">glfwCreateWindow</a>(...)的前三个参数一目了然不必多说，其第四个参数用于指定全屏模式的显示器，若为<span class="kw">nullptr</span>则使用窗口模式，第五个参数可传入一个其他窗口的指针，用于与其他窗口分享内容。
</p>
<p>
    如果你想实现全屏，则还需要用<a href="https://www.glfw.org/docs/latest/group__monitor.html#gac3adb24947eb709e1874028272e5dfc5">glfwGetPrimaryMonitor</a>()取得当前显示器信息的指针，即便你在初始化时不想全屏，也有必要先取得它以备之后使用：
</p>
<pre class="code">
pMonitor = <span class="fn">glfwGetPrimaryMonitor</span>();
</pre>
<p>
    通常全屏时的图像区域应当跟屏幕分辨率一致，因此还需要使用<a href="https://www.glfw.org/docs/latest/group__monitor.html#gaba376fa7e76634b4788bddc505d6c9d5">glfwGetVideoMode</a>(...)取得显示器当前的视频模式：
</p>
<pre class="code">
<span class="kw">const</span> <span class="type">GLFWvidmode</span>* pMode = <span class="fn">glfwGetVideoMode</span>(pMonitor);
</pre>
<ul>
    <li>
        <p>
            视频模式可能因为用户的操作而在程序运行过程中发生变更，因此总是在需要时获取，而不将其存储到全局变量。
        </p>
    </li>
</ul>
<p>
    于是，若要实现全屏，根据<span class="par">fullScreen</span>的值决定是否以全屏模式初始化窗口：
</p>
<pre class="code">
pWindow = <span class="par">fullScreen</span> ?
    <span class="fn">glfwCreateWindow</span>(pMode-&gt;width, pMode-&gt;height, windowTitle, pMonitor, <span class="kw">nullptr</span>) :
    <span class="fn">glfwCreateWindow</span>(<span class="par">size</span>.width, <span class="par">size</span>.height, windowTitle, <span class="kw">nullptr</span>, <span class="kw">nullptr</span>);
</pre>
<p>
    验证<span class="var">pWindow</span>的值，若窗口创建失败，用<a href="https://www.glfw.org/docs/latest/group__init.html#gaaae48c0a18607ea4a4ba951d939f0901">glfwTerminate</a>()来清理GLFW并让函数返回<span class="kw">false</span>：
</p>
<pre class="code">
<span class="kw">if</span> (!pWindow) {
    std::cout &lt;&lt; std::<span class="fn">format</span>(<span class="str">&quot;[ InitializeWindow ]\nFailed to create a glfw window!\n&quot;</span>);
    <span class="fn">glfwTerminate</span>();
    <span class="kw">return false</span>;
}
</pre>
<p>
    当然<span class="fn">TerminateWindow</span>()里也会调用<a href="https://www.glfw.org/docs/latest/group__init.html#gaaae48c0a18607ea4a4ba951d939f0901">glfwTerminate</a>()，于是，至此为止<span class="fn">InitializeWindow</span>(...)和<span class="fn">TerminateWindow</span>()的内容总结如下：
</p>
<pre class="code">
<span class="kw">bool</span> <span class="fn">InitializeWindow</span>(<span class="type">VkExtent2D</span> <span class="par">size</span>, <span class="kw">bool</span> <span class="par">fullScreen</span> = <span class="kw">false</span>, <span class="kw">bool</span> <span class="par">isResizable</span> = <span class="kw">true</span>, <span class="kw">bool</span> <span class="par">limitFrameRate</span> = <span class="kw">true</span>) {
    <span class="kw">if</span> (!<span class="fn">glfwInit</span>()) {
        std::cout &lt;&lt; std::<span class="fn">format</span>(<span class="str">&quot;[ InitializeWindow ] ERROR\nFailed to initialize GLFW!\n&quot;</span>);
        <span class="kw">return false</span>;
    }
    <span class="fn">glfwWindowHint</span>(<span class="mcr">GLFW_CLIENT_API</span>, <span class="mcr">GLFW_NO_API</span>);
    <span class="fn">glfwWindowHint</span>(<span class="mcr">GLFW_RESIZABLE</span>, <span class="par">isResizable</span>);
    pMonitor = <span class="fn">glfwGetPrimaryMonitor</span>();
    <span class="kw">const</span> <span class="type">GLFWvidmode</span>* pMode = <span class="fn">glfwGetVideoMode</span>(pMonitor);
    pWindow = <span class="par">fullScreen</span> ?
        <span class="fn">glfwCreateWindow</span>(pMode-&gt;width, pMode-&gt;height, windowTitle, pMonitor, <span class="kw">nullptr</span>) :
        <span class="fn">glfwCreateWindow</span>(<span class="par">size</span>.width, <span class="par">size</span>.height, windowTitle, <span class="kw">nullptr</span>, <span class="kw">nullptr</span>);
    <span class="kw">if</span> (!pWindow) {
        std::cout &lt;&lt; std::<span class="fn">format</span>(<span class="str">&quot;[ InitializeWindow ]\nFailed to create a glfw window!\n&quot;</span>);
        <span class="fn">glfwTerminate</span>();
        <span class="kw">return false</span>;
    }
    <span class="cmt">/*待Ch1-3和Ch1-4填充*/</span>
    <span class="kw">return true</span>;
}
<span class="kw">void</span> <span class="fn">TerminateWindow</span>() {
    <span class="cmt">/*待Ch1-4填充*/</span>
    <span class="fn">glfwTerminate</span>();
}
</pre>
<div class="admonition important">
    <p class="admonition-title">Important</p>
    <p>
        注意，你不需要手动 <span class="kw">delete</span> <span class="var">pMode</span>，GLFW的注释及官方文档中写到：
        <br>
        <span class="ref">The returned array is allocated and freed by GLFW. You should not free it yourself. It is valid until the specified monitor is disconnected or the library is terminated.</span>
    </p>
</div></section>
<section id="id3">
<h2>在窗口标题上显示帧率<a class="headerlink" href="#id3" title="Permalink to this heading"></a></h2>
<p>
    下面是一个用于在标题上显示帧率的函数，每帧调用一次。约每一秒更新一次帧率，其逻辑为：
    <br>
    记录时间点<span class="var">t0</span>，若之后某次调用该函数时取得的时间<span class="var">t1</span>已超过<span class="var">t0</span>一秒，用<span class="var">t1</span>与<span class="var">t0</span>的差除以这中间经历的帧数，得到帧率，并将<span class="var">t1</span>赋值给<span class="var">t0</span>。
    <br>
    得到帧率后，将其转为字符串衔接在原标题后，然后用<a href="https://www.glfw.org/docs/latest/group__window.html#ga5d877f09e968cef7a360b513306f17ff">glfwSetWindowTitle</a>(...)设置窗口标题。
</p>
<pre class="code">
<span class="kw">void</span> <span class="fn">TitleFps</span>() {
    <span class="kw">static double</span> time0 = <span class="fn">glfwGetTime</span>();
    <span class="kw">static double</span> time1;
    <span class="kw">static double</span> dt;
    <span class="kw">static int</span> dframe = -1;
    <span class="kw">static</span> std::<span class="type">stringstream</span> info;
    time1 = <span class="fn">glfwGetTime</span>();
    dframe++;
    <span class="kw">if</span> ((dt = time1 - time0) &gt;= 1) {
        info.<span class="fn">precision</span>(1);
        info &lt;&lt; windowTitle &lt;&lt; <span class="str">&quot;    &quot;</span> &lt;&lt; std::<span class="fn">fixed</span> &lt;&lt; dframe / dt &lt;&lt; <span class="str">&quot; FPS&quot;</span>;
        <span class="fn">glfwSetWindowTitle</span>(pWindow, <span class="fn">info.str</span>().<span class="fn">c_str</span>());
        info.<span class="fn">str</span>(<span class="str">&quot;&quot;</span>); <span class="cmt">//别忘了在设置完窗口标题后清空所用的stringstream</span>
        time0 = time1;
        dframe = 0;
    }
}
</pre>
<p>
<a href="https://www.glfw.org/docs/3.0/group__time.html#gaa6cf4e7a77158a3b8fd00328b1720a4a">glfwGetTime</a>()返回自GLFW初始化以来的时间，单位为秒。
我在<a href="https://github.com/EasyVulkan/EasyVulkan.github.io/blob/main/solution/EasyVulkan/WinGeneral.hpp">WinGeneral.hpp</a>中也提供了通过C++标准库函数来取得时间的方法。
</p></section>
<section id="id4">
<h2>试着运行一下吧！<a class="headerlink" href="#id4" title="Permalink to this heading"></a></h2>
<p>
    到<span class="path">main.cpp</span>中书写主函数：
</p>
<pre class="code">
<span class="pragma">#include</span> <span class="str">&quot;GlfwGeneral.hpp&quot;</span>

<span class="kw">int</span> <span class="fn">main</span>() {
    <span class="kw">if</span> (!<span class="fn">InitializeWindow</span>({ 1280, 720 }))
        <span class="kw">return</span> -1; <span class="cmt">//来个你讨厌的返回值</span>
    <span class="kw">while</span> (!<span class="fn">glfwWindowShouldClose</span>(pWindow)) {

        <span class="cmt">/*渲染过程，待填充*/</span>

        <span class="fn">glfwPollEvents</span>();
        <span class="fn">TitleFps</span>();
    }
    <span class="fn">TerminateWindow</span>();
    <span class="kw">return</span> 0;
}
</pre>
<ul>
    <li>
        <p>
            <a href="https://www.glfw.org/docs/latest/group__window.html#ga37bd57223967b4211d60ca1a0bf3c832">glfwPollEvents</a>()用于处理GLFW相关的事件，如接收窗口变化的信息，及接受输入并执行回调函数。
        </p>
    </li>
    <li>
        <p>
            若<a href="https://www.glfw.org/docs/latest/group__window.html#ga24e02fbfefbb81fc45320989f8140ab5">glfwWindowShouldClose</a>(...)返回<span class="kw">true</span>，说明在前一次中收到了来自操作系统的信息，告诉GLFW应当关闭窗口。
        </p>
    </li>
</ul>
<p>
    如果你在编译后看到了一个全白的窗口，并且帧率还炸了，那就算是一切顺利！
</p>
<p>
    如果你在控制台中见到信息“libpng warning: iCCP: cHRM chunk does not match sRGB”，那是因为你正在使用QQ输入法（这真的很奇怪，所以我特地提一嘴）。
</p></section>
<section id="id5">
<h2>在全屏和窗口模式间切换<a class="headerlink" href="#id5" title="Permalink to this heading"></a></h2>
<p>本节为非必要项，与今后的学习内容无关。</p>
<p>
    用<a href="https://www.glfw.org/docs/latest/group__window.html#ga81c76c418af80a1cce7055bccb0ae0a7">glfwSetWindowMonitor</a>(...)可以在全屏和窗口模式间切换。
</p>
<table class="docutils align-default">
    <colgroup>
        <col style="width: 30%">
        <col style="width: 70%">
    </colgroup>
    <thead>
        <tr class="row-odd">
            <th class="head" colspan="2"><p><span class="kw">void</span> <a href="https://www.glfw.org/docs/latest/group__window.html#ga81c76c418af80a1cce7055bccb0ae0a7">glfwSetWindowMonitor</a>(...) 的参数说明</p></th>
        </tr>
    </thead>
    <tbody>
        <tr class="row-even">
            <td><p><span class="type">GLFWwindow</span>* window</p></td>
            <td><p>窗口的指针</p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="type">GLFWmonitor</span>* monitor</p></td>
            <td><p>显示器信息的指针，用于指定全屏模式的显示器</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="kw">int</span> xpos</p></td>
            <td><p>图像区域左上角在屏幕上的横坐标</p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="kw">int</span> ypos</p></td>
            <td><p>图像区域左上角在屏幕上的纵坐标</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="kw">int</span> width</p></td>
            <td><p>图像区域的宽度</p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="kw">int</span> height</p></td>
            <td><p>图像区域的高度</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="kw">int</span> refreshRate</p></td>
            <td><p>屏幕刷新率</p></td>
        </tr>
    </tbody>
</table>
<ul>
    <li>
        <p>
            所指图像区域不包括窗口的边框和标题栏。
        </p>
    </li>
    <li>
        <p>
            切换到全屏模式时，<span class="var">xpos</span>和<span class="var">ypos</span>被无视。
        </p>
    </li>
    <li>
        <p>
            切换到窗口模式时，<span class="var">ypos</span>应避免小于标题栏高度。
        </p>
    </li>
</ul>
<p>
    在<span class="path">GlfwGeneral.hpp</span>中定义函数<span class="fn">MakeWindowFullScreen</span>()和<span class="fn">MakeWindowWindowed</span>(...)分别用于从窗口变为全屏和从全屏变回窗口：
</p>
<pre class="code">
<span class="kw">void</span> <span class="fn">MakeWindowFullScreen</span>() {
    <span class="kw">const</span> <span class="type">GLFWvidmode</span>* pMode = <span class="fn">glfwGetVideoMode</span>(pMonitor);
    <span class="fn">glfwSetWindowMonitor</span>(pWindow, pMonitor, 0, 0, pMode-&gt;width, pMode-&gt;height, pMode-&gt;refreshRate);
}
<span class="kw">void</span> <span class="fn">MakeWindowWindowed</span>(<span class="type">VkOffset2D</span> <span class="par">position</span>, <span class="type">VkExtent2D</span> <span class="par">size</span>) {
    <span class="kw">const</span> <span class="type">GLFWvidmode</span>* pMode = <span class="fn">glfwGetVideoMode</span>(pMonitor);
    <span class="fn">glfwSetWindowMonitor</span>(pWindow, <span class="kw">nullptr</span>, <span class="par">position</span>.x, <span class="par">position</span>.y, <span class="par">size</span>.width, <span class="par">size</span>.height, pMode-&gt;refreshRate);
}
</pre>
<p>
    接着就是获取鼠标/键盘输入，在按下你期望的按键后调用上述函数了。
    <br>
    关于如何获取输入在此不做赘述，请自行参阅<a href="https://www.glfw.org/docs/latest/input_guide.html">GLFW的Input guide</a>。
</p></section>
</section>


           </div>
          </div>
          <footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
        <a href="Ch1-0%20%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C.html" class="btn btn-neutral float-left" title="Ch1-0 准备工作" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> 上一页</a>
        <a href="Ch1-2%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%B5%81%E7%A8%8B.html" class="btn btn-neutral float-right" title="Ch1-2 初始化流程" accesskey="n" rel="next">下一页 <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
    </div>

  <hr/>

  <div role="contentinfo">
    <p>&#169; 版权所有 2021-2025, 葉橙.</p>
  </div>

  利用 <a href="https://www.sphinx-doc.org/">Sphinx</a> 构建，使用了 
    <a href="https://github.com/readthedocs/sphinx_rtd_theme">主题</a>
    由 <a href="https://readthedocs.org">Read the Docs</a>开发.
   

</footer>
        </div>
      </div>
    </section>
  </div>
  <script>
      jQuery(function () {
          SphinxRtdTheme.Navigation.enable(true);
      });
  </script> 

</body>
</html>